#include "Common.h"
#include "MeshSerializer.h"
#include "Serializer.h"
#include "Mesh.h"
#include "Skeleton.h"
#include "MeshBuilder.h"
#include "MeshManager.h"
#include "SkeletonManager.h"
#include "Utility.h"

/// stream overhead = ID + size
const int STREAM_OVERHEAD_SIZE = sizeof(WORD) + sizeof(DWORD);
const WORD HEADER_STREAM_ID = 0x1000;

MeshSerializer::MeshSerializer()
	: mMeshSerializerVersion("[MeshSerializer_v1.41]")
	, mSkeletonSerializerVersion("[Serializer_v1.10]")
{
}

MeshSerializer::~MeshSerializer()
{
}

bool MeshSerializer::ImportMesh(const std::string& fileName, Mesh* mesh)
{
	std::string fullPath;
	if (!Utility::FindFile(fileName, fullPath))
		return false;

	Serializer ser(false);
	if (!ser.Initialize(fullPath.c_str()))
		return false;

	// Check header
    if (!readFileHeader(ser, mMeshSerializerVersion))
		return false;

    if (readChunk(ser) != M_MESH)
		return false;

	MeshData meshData;
	if (!readMesh(ser, meshData))
		return false;

	// build the mesh.
	MeshBuilder meshBuilder;
	if (!meshBuilder.BuildMesh(meshData, mesh))
		return false;

	// setup the skeleton.
	if (!meshData.Skeleton.empty())
		mesh->setSkeleton(SkeletonManager::Instance().Load(meshData.Skeleton));

	mesh->Init(meshData.SkeletallyAnimated);

	return true;
}

bool MeshSerializer::readMesh(Serializer& ser, MeshData& meshData)
{
	ser << meshData.SkeletallyAnimated;

	VertexBoneAssignment vbAssignment;
	bool finished = false;
	while (ser.Good() && !finished)
	{
        switch (readChunk(ser))
        {
		case M_GEOMETRY:
			readGeometry(ser, meshData.SharedVertexBuff);
			break;
		case M_SUBMESH:
			readSubMesh(ser, meshData);
			break;
		case M_MESH_SKELETON_LINK:
			ser << meshData.Skeleton;
			break;
		case M_MESH_BONE_ASSIGNMENT:
			ser << vbAssignment.VertexIndex << vbAssignment.BoneIndex << vbAssignment.Weight;
			meshData.VertexBoneAssignments.insert(
				VertexBoneAssignmentList::value_type(vbAssignment.VertexIndex, vbAssignment));
			break;
		case M_MESH_BOUNDS:
			{
				Vector3 Min, Max;
				ser << Min << Max << meshData.BoundRadius;
				meshData.Bounds.Init(Min, Max);
			}
			break;
		case M_SUBMESH_NAME_TABLE:
			while (ser.Good() && readChunk(ser) == M_SUBMESH_NAME_TABLE_ELEMENT)
			{
				WORD index;
				std::string name;
				ser << index << name;
				if (index < meshData.SubMeshes.size())
					meshData.SubMeshes[index].Name = name;
			}
			ser.Skip(-STREAM_OVERHEAD_SIZE);
			break;
		case M_MESH_LOD:
		case M_EDGE_LISTS:
		case M_POSES:
		case M_ANIMATIONS:
		case M_TABLE_EXTREMES:
			ser.Skip(mCurrentstreamLen - STREAM_OVERHEAD_SIZE);
			break;
		default:
			finished = true;
			ser.Skip(-STREAM_OVERHEAD_SIZE);
			break;
        }
	}

	return true;
}

bool MeshSerializer::readGeometry(Serializer& ser, VertexDataBuff& vertexDataBuff)
{
	ser << vertexDataBuff.VertexCount;

	bool finished = false;
	while (ser.Good() && !finished)
	{
        switch (readChunk(ser))
        {
		case M_GEOMETRY_VERTEX_DECLARATION:
			while (readChunk(ser) == M_GEOMETRY_VERTEX_ELEMENT)
			{
				VertexDeclare vertexDeclare;
				ser << vertexDeclare.source << vertexDeclare.type << vertexDeclare.semantic << vertexDeclare.offset << vertexDeclare.index;
				vertexDataBuff.VertexDeclares.push_back(vertexDeclare);
			}
			if (ser.Good())
				ser.Skip(-STREAM_OVERHEAD_SIZE);
			break;
		case M_GEOMETRY_VERTEX_BUFFER:
			{
				ser << vertexDataBuff.BindIndex << vertexDataBuff.VertexSize;
				if (readChunk(ser) != M_GEOMETRY_VERTEX_BUFFER_DATA)
					return false;

				vertexDataBuff.Buff.reserve(vertexDataBuff.VertexCount * vertexDataBuff.VertexSize);
				vertexDataBuff.Buff.resize(vertexDataBuff.VertexCount * vertexDataBuff.VertexSize);
				ser.Serialize(&vertexDataBuff.Buff[0], vertexDataBuff.Buff.size());
			}
			break;
		default:
			finished = true;
			ser.Skip(-STREAM_OVERHEAD_SIZE);
			break;
		}
	}

	return true;
}

bool MeshSerializer::readSubMesh(Serializer& ser, MeshData& meshData)
{
	meshData.SubMeshes.push_back(SubMeshData());
	SubMeshData& subMeshData = meshData.SubMeshes.back();

	ser << subMeshData.Material;
	ser << subMeshData.UseSharedGeometry;
	ser << subMeshData.IndexBuff.IndexCount;
	ser << subMeshData.IndexBuff.Uint32Index;
	if (subMeshData.IndexBuff.IndexCount > 0)
	{
		DWORD buffSize = (subMeshData.IndexBuff.Uint32Index ? sizeof(DWORD) : sizeof(WORD)) *
			subMeshData.IndexBuff.IndexCount;
		subMeshData.IndexBuff.Buff.reserve(buffSize);
		subMeshData.IndexBuff.Buff.resize(buffSize);
		ser.Serialize(&subMeshData.IndexBuff.Buff[0], buffSize);
	}

	if (!subMeshData.UseSharedGeometry)
	{
		if (readChunk(ser) != M_GEOMETRY)
			return false;

		readGeometry(ser, subMeshData.PrivateVertexBuff);
	}

	std::string textureAlise;
	VertexBoneAssignment vbAssignment;
	bool finished = false;
	while (ser.Good() && !finished)
	{
		switch (readChunk(ser))
		{
		case M_SUBMESH_OPERATION:
			ser << subMeshData.OperationType;
			break;
		case M_SUBMESH_BONE_ASSIGNMENT:
			ser << vbAssignment.VertexIndex << vbAssignment.BoneIndex << vbAssignment.Weight;
			subMeshData.VertexBoneAssignments.insert(
				VertexBoneAssignmentList::value_type(vbAssignment.VertexIndex, vbAssignment));
			break;
		case M_SUBMESH_TEXTURE_ALIAS:
			ser << textureAlise;
			ser << textureAlise;
			break;
		default:
			ser.Skip(-STREAM_OVERHEAD_SIZE);
			finished = true;
			break;
		}
	}

	return true;
}

bool MeshSerializer::readSkeletonLink(Serializer& ser, MeshData& meshData)
{
	return true;
}

bool MeshSerializer::readMeshBoneAssignment(Serializer& ser, MeshData& meshData)
{
	return true;
}

bool MeshSerializer::readMeshLodInfo(Serializer& ser, MeshData& meshData)
{
	return true;
}

bool MeshSerializer::readBoundsInfo(Serializer& ser, MeshData& meshData)
{
	return true;
}

bool MeshSerializer::ImportSkeleton(const std::string& fileName, Skeleton* skeleton)
{
	std::string fullPath;
	if (!Utility::FindFile(fileName, fullPath))
		return false;

	Serializer ser(false);
	if (!ser.Initialize(fullPath.c_str()))
		return false;

	// Check header
    if (!readFileHeader(ser, mSkeletonSerializerVersion))
		return false;

	bool finished = false;
    while (ser.Good() && !finished)
    {
        switch (readChunk(ser))
        {
        case SKELETON_BONE:
            readBone(ser, skeleton);
            break;
        case SKELETON_BONE_PARENT:
            readBoneParent(ser, skeleton);
            break;
        case SKELETON_ANIMATION:
            readAnimation(ser, skeleton);
			break;
		default:
			finished = true;
			break;
        }
    }

	skeleton->SetBindingPose();

	return true;
}


bool MeshSerializer::readFileHeader(Serializer& ser, const char* version)
{
	WORD headerID = HEADER_STREAM_ID;

	ser << headerID;
	if (headerID != HEADER_STREAM_ID)
		return false;

	ser << mVersion;
	if (mVersion.empty())
		return false;

	if (mVersion != version)
		return false;

	return true;
}

WORD MeshSerializer::readChunk(Serializer& ser)
{
	WORD chunkId = 0;
	ser << chunkId;
	ser << mCurrentstreamLen;
	return chunkId;
}

bool MeshSerializer::readBone(Serializer& ser, Skeleton* skeleton)
{
	std::string name;
	Bone bone;

	ser << name;
	if (name.empty())
		return false;

	ser << bone.Index;
	if (bone.Index >= MAX_BONE_NUM)
		return false;

	ser << bone.InitTranslate;
	ser << bone.InitOrientation;

	const DWORD boneSizeWithoutScale = STREAM_OVERHEAD_SIZE + sizeof(bone.Index) + sizeof(bone.InitTranslate) + sizeof(bone.InitOrientation);
	if (mCurrentstreamLen > boneSizeWithoutScale)
		ser << bone.InitScale;
	else
		bone.InitScale = Vector3::One;

	bone.ParentIndex = 0;

	skeleton->Bones.Bones.Resize(bone.Index + 1);
	skeleton->Bones.Bones.Get(bone.Index) = bone;
	skeleton->Bones.BoneNameIndexMap[name] = bone.Index;
	return true;
}

bool MeshSerializer::readBoneParent(Serializer& ser, Skeleton* skeleton)
{
	WORD childHandle, parentHandle;
	ser << childHandle;
	ser << parentHandle;

	if (childHandle >= skeleton->Bones.Bones.Size() || parentHandle >= skeleton->Bones.Bones.Size())
		return false;

	skeleton->Bones.Bones.Get(childHandle).ParentIndex = parentHandle;
	return true;
}

bool MeshSerializer::readAnimation(Serializer& ser, Skeleton* skeleton)
{
	std::string name;
	ser << name;
	if (name.empty())
		return false;

	Animation& animation = skeleton->Animations[name];
	animation.Name = name;
	ser << animation.Length;

	// read animation tracks.
    while (readChunk(ser) == SKELETON_ANIMATION_TRACK && ser.Good())
    {
		// create a new track.
		animation.Tracks.resize(animation.Tracks.size() + 1);
		AnimationTrack& track = animation.Tracks.back();

		ser << track.BoneIndex;
		if (track.BoneIndex >= skeleton->Bones.Bones.Size())
			return false;

		// read key frames.
		while (readChunk(ser) == SKELETON_ANIMATION_TRACK_KEYFRAME && ser.Good())
		{
			track.KeyFrames.resize(track.KeyFrames.size() + 1);
			KeyFrame& keyFrame = track.KeyFrames.back();

			ser << keyFrame.Time;
			ser << keyFrame.Orientation;
			ser << keyFrame.Translate;

			const DWORD keyFrameSizeNoScale = STREAM_OVERHEAD_SIZE + sizeof(float) * (1 + 4 + 3);
			if (mCurrentstreamLen > keyFrameSizeNoScale)
				ser << keyFrame.Scale;
			else
				keyFrame.Scale = Vector3::One;
		}

		if (ser.Good())
			ser.Skip(-STREAM_OVERHEAD_SIZE);
	}

	if (ser.Good())
		ser.Skip(-STREAM_OVERHEAD_SIZE);

	return true;
}
